package com.stox.shared.charting.util;

import java.util.ArrayList;
import java.util.List;

import com.stox.shared.charting.model.AggregateBar;
import com.stox.shared.charting.model.Kagi;
import com.stox.shared.charting.model.LineBreak;
import com.stox.shared.charting.model.Pnf;
import com.stox.shared.charting.model.Renko;
import com.stox.shared.charting.model.Pnf.Box;
import com.stox.shared.core.model.Bar;

public class BarTypeUtil {

	public static List<AggregateBar> aggregate(final List<Bar> bars, final double size, final boolean useHighLow) {
		final List<AggregateBar> aggregateBars = new ArrayList<>();
		double pivot = 0;
		double previousPivot = 0;
		AggregateBar aggregateBar = null;
		final double reversalValue = size;
		final List<Bar> waveBars = new ArrayList<>();
		for (int index = bars.size() - 1; index >= 0; index--) {
			final Bar bar = bars.get(index);
			final double value = useHighLow ? (pivot > previousPivot ? bar.getLow() : bar.getHigh()) : bar.getClose();
			waveBars.add(bar);
			if (pivot >= previousPivot) {
				if (value > pivot) {
					pivot = value;
				} else if ((pivot - value) > reversalValue) {
					aggregateBar = new AggregateBar(waveBars, false);
					aggregateBars.add(0, aggregateBar);
					waveBars.clear();
					previousPivot = pivot;
					pivot = value;
				}
			} else {
				if (value < pivot) {
					pivot = value;
				} else {
					aggregateBar = new AggregateBar(waveBars, true);
					aggregateBars.add(0, aggregateBar);
					waveBars.clear();
					previousPivot = pivot;
					pivot = value;
				}
			}
		}
		if ((null != aggregateBar) && !aggregateBars.contains(aggregateBar)) {
			aggregateBars.add(aggregateBar);
		}
		return aggregateBars;
	}

	public static List<Bar> lineBreak(final List<Bar> bars, final int reversalAmount) {
		if ((null == bars) || bars.isEmpty()) {
			return bars;
		}
		final List<Bar> result = new ArrayList<Bar>();
		Bar bar = bars.get(bars.size() - 1);
		LineBreak lineBreak = new LineBreak(true, reversalAmount, bar, null);
		for (int index = bars.size() - 2; index >= 0; index--) {
			bar = bars.get(index);
			final LineBreak aLineBreak = lineBreak.consume(bar);
			if (aLineBreak != lineBreak) {
				result.add(0, lineBreak);
				lineBreak = aLineBreak;
			}
		}
		if (!result.contains(lineBreak)) {
			result.add(0, lineBreak);
		}
		return result;
	}

	public static List<Bar> renko(final List<Bar> bars, final double reversalSize, final boolean reversalSizeInPercentage) {
		if ((null == bars) || bars.isEmpty()) {
			return bars;
		}
		final List<Bar> result = new ArrayList<Bar>();
		final double size = reversalSizeInPercentage ? (reversalSize * bars.get(0).getClose()) / 100 : reversalSize;
		Bar bar = bars.get(bars.size() - 1);
		Renko renko = new Renko(true, size, bar.getClose(), bar.getDate());
		for (int index = bars.size() - 1; index >= 0; index--) {
			bar = bars.get(index);
			final Renko aRenko = renko.consume(bar);
			if (aRenko != renko) {
				result.add(0, renko);
				renko = aRenko;
				index += 1;
			}
		}
		if (!result.contains(renko)) {
			result.add(0, renko);
		}
		return result;
	}

	public static List<Bar> kagi(final List<Bar> bars, final double reversalSize, final boolean reversalSizeInPercentage) {
		if ((null == bars) || bars.isEmpty()) {
			return bars;
		}
		final List<Bar> result = new ArrayList<Bar>();
		final double size = reversalSizeInPercentage ? (reversalSize * bars.get(0).getClose()) / 100 : reversalSize;
		Kagi kagi = new Kagi(true, size, null);
		Bar bar = bars.get(bars.size() - 1);
		kagi.setLow(bar.getClose());

		for (int index = bars.size() - 1; index >= 0; index--) {
			bar = bars.get(index);
			final Kagi aKagi = kagi.consume(bar);
			if (aKagi != kagi) {
				result.add(0, kagi);
				kagi = aKagi;
			}
		}
		if (!result.contains(kagi)) {
			result.add(0, kagi);
		}
		return result;
	}

	public static List<Bar> haikinAshi(final List<Bar> bars) {
		if ((null == bars) || (bars.size() < 2)) {
			return bars;
		}
		final List<Bar> result = new ArrayList<Bar>(bars.size());
		Bar previousAshi = null;
		for (int index = bars.size() - 1; index >= 0; index--) {
			final Bar bar = bars.get(index);
			if (null != previousAshi) {
				final Bar ashi = new Bar();
				ashi.setClose((bar.getOpen() + bar.getHigh() + bar.getLow() + bar.getClose()) / 4);
				ashi.setOpen((previousAshi.getOpen() + previousAshi.getClose()) / 2);
				ashi.setHigh(Math.max(Math.max(bar.getHigh(), ashi.getOpen()), ashi.getClose()));
				ashi.setLow(Math.min(Math.min(bar.getLow(), ashi.getOpen()), ashi.getClose()));
				ashi.setDate(bar.getDate());
				ashi.setScrip(bar.getScrip());
				ashi.setScripId(bar.getScripId());
				ashi.setVolume(bar.getVolume());
				result.add(0, ashi);
			} else {
				result.add(0, bar);
			}
			previousAshi = bar;
		}
		return result;
	}

	public static List<Bar> pnf(final List<Bar> bars, final double boxSize, final int reversalAmount, final boolean boxSizeInPercentage) {
		if ((null == bars) || bars.isEmpty()) {
			return bars;
		}
		final List<Bar> result = new ArrayList<Bar>();
		final double size = boxSizeInPercentage ? (boxSize * bars.get(0).getClose()) / 100 : boxSize;
		final double reversalSize = size * reversalAmount;


		Bar bar = bars.get(bars.size() - 1);
		Box box = new Box(true, size, reversalSize, bar.getClose(), bar);
		Pnf pnf = new Pnf(true, size, reversalSize, bar.getDate());

		for (int index = bars.size() - 1; index >= 0; index--) {
			bar = bars.get(index);
			final Box aBox = box.consume(bar);
			if (aBox != box) {
				pnf.doConsume(box);
				if (aBox.isUp() != pnf.isUp()) {
					result.add(0, pnf);
					pnf = new Pnf(aBox.isUp(), size, reversalSize, bar.getDate());
				}
				box = aBox;
				index++;
			}
		}
		if (!result.contains(pnf)) {
			result.add(0, pnf);
		}
		return result;
	}

	public static void trim(final Bar bar, final double size) {
		final double high = bar.getHigh();
		bar.setHigh(high - (high % size));
		final double low = bar.getLow();
		bar.setLow(low + (size - (low % size)));
	}

	
	
}
